package ddu.com.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;

/**
 * @author:zhangxia
 * @createTime: 2018/4/17 14:58
 * @version:1.0
 * @desc:通过aop拦截后执行具体操作，
 *  @Before是在所拦截方法执行之前执行一段逻辑。
    @After 是在所拦截方法执行之后执行一段逻辑。
    @Around是可以同时在所拦截方法的前后执行一段逻辑。
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class LoginIntercept {

    /**
     * @auth: zhangxia
     * @desc: 用于检测此类有没有被扫描自动加载
     * @methodName: LoginIntercept
     * @params: []
     * @return:
     * @createTime:
     * @version:2.1.6
     */
    public LoginIntercept() {

        log.info("******LoginIntercept*******加载完成");
    }

    /**
     * @auth: zhangxia
     * @desc: 声明切入点
     * @methodName: recordLog
     * @params: []
     * @return: void
     * @createTime: 2018/4/17 15:34
     * execution(public * ddu.com.controller..*.*(..))  ：代表controller包下所有类的所有方法都会被代理
     * @version:2.1.6
     */
    @Pointcut("execution(public * ddu.com.controller..*.*(..))")
    public void recordLog(){}


    /**
     * @auth: zhangxia
     * @desc: 方法的前置通知
     * @methodName: before
     * @params: [joinPoint]joinPoint 封装了代理方法信息的对象,若用不到则可以忽略不写
     * @return: void
     * @createTime: 2018/4/20 9:21
     * @version:2.1.6
     */
    @Before(value = "recordLog()")
    public void before(JoinPoint joinPoint){
        log.info("aop已经记录下操作日志@Before 方法执行前==start");
        String className = joinPoint.getThis().toString();//获取当前调用对象的类名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        if (!Objects.isNull(methodSignature)){
            Method method = methodSignature.getMethod();
            log.info("位于类名：{} 中的调用方法名：{}",className,method.getName());
        }
        log.info("切点访问的方法名：{}",joinPoint.getSignature().getName());
        log.info("目标方法所属类的简单类名:{}",joinPoint.getSignature().getDeclaringType().getSimpleName());
        log.info("目标方法所属类的类名:{}",joinPoint.getSignature().getDeclaringType());
        log.info("目标方法声明类型:{}", Modifier.toString(joinPoint.getSignature().getModifiers()));
        //获取传入目标方法的参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            log.info("第" + (i+1) + "个参数为:" + args[i]);
        }
        log.info("被代理的对象:" + joinPoint.getTarget());
        log.info("代理对象自己:" + joinPoint.getThis());
        log.info("aop已经记录下操作日志@Before 方法执行前==start");
    }


    /**
     * @auth: zhangxia
     * @desc:  配置环绕通知
     * @methodName: around
     * @params: [pjp, at]JoinPoint的子接口,添加了
     *            Object proceed() throws Throwable 执行目标方法
     *            Object proceed(Object[] var1) throws Throwable 传入的新的参数去执行目标方法
     *            两个方法
     * @return: void
     * @createTime: 2018/4/18 11:24
     * @version:2.1.6
     */
    @Around(value = "@annotation(at)")
    public void around(ProceedingJoinPoint pjp,AccessToken at) throws Throwable {
        log.info("aop已经记录下操作日志@Around 方法执行前");
        //获取当前请求
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        pjp.proceed();
        //用新的参数值执行目标方法
//        pjp.proceed(new Object[]{"newSpring","newAop"});
        log.info("aop已经记录下操作日志@Around 方法执行后");
    }

    @After(value = "recordLog()")
    public void after(){
        log.info("aop已经记录下操作日志@After 方法执行后");
    }

    @AfterThrowing("recordLog()")
    public void catchInfo(){
        log.info("处理结果异常");
    }

}
